/*global pi */

pi.mod.define('pi.jsByteBuffer', function (pi) {
	"use strict";
	var ByteBuffer, log = pi.log,
		logt = pi.log,
		rb = pi.vcall.readBuffer,
		wb = pi.vcall.writeBuffer,
		ByteBuffer_MinCapcity = 10,
		module = {};

	/*
	 * js的bytebuffer实现.因为不知道如何用c控制ArrayBuffer的扩容
	 * 支持float,int64,short,byte等c有而js没有的数据类型;
	 * 但js用doulbe表示int型,所以int64类型如果超出了2^53(详见ieee754)则有精度损失
	 * readUint等可以通过readInt等实现(调用toUint()...),但为了代码统一,都在c层实现.
	 * [rOffset, wOffset)包含有效数据,
	 *
	 * write byte/short/int: 先将js值转为int,再截断为byte/short/int
	 * write long(int64): js to int64,再赋值,没有截断,但double保存int64的精度只有52位左右(see ieee754)
	 * 总结:存在两步转换,
	 *  1.js to integer,如果integer超出了52位,则有精度损失
	 *  2.integer to byte/short/int, 简单的截断,注意精度损失.
	 *
	 * why写入时可以指定分有/无符号?
	 * 写入的时候是进行的截断,因为不管有无符号,截断的结果都一样,所以不需要指定
	 * 读取的时候是进行扩展,当最高位为1时,有/无符号扩展的结果不一样,因此需要指定
	 *
	 * write exmaple:(输出字节顺序为从高到底)
	 * bb.wl(-3000000000); //-30亿
	 * 00 a2 2f 4d ff ff ff ff
	 * bb.wi(-3000000000);
	 * 00 a2 2f 4d
	 * 从中也可以看出就是简单的截断.
	 *
	 * 有无符号的等价问题:
	 * 对于byte类型来说,-1与255等价,但对于short却并不如此:
	 * bb.wb(-1);
	 * bb.wb(255);//both 0xff
	 * bb.wsh(-1);//0xffff
	 * bb.wsh(255);//0x00ff
	 */
	ByteBuffer = function (capacity, maxLength, initValue) {
		var self = this;

		//toString
		self.toString = function (bBufCrc) {
			var crc = 0;
			if (bBufCrc) {
				crc = pi.jsbuffer.getCrc32(self.buf, self.buf.byteLength);
			}
			return pi.string.formatAuto("[rOffset=%s, wOffset=%s, capactiy=%s, buf_crc=%s, maxLength=%s]", self.rOffset, self.wOffset, self.buf.byteLength, crc, self.maxLength);
		};

		//print
		self.print = function (start, len, countPerLine) {
			log("[" + self.toString() + "]");
			pi.jsbuffer.printBuffer(self.buf, start, len, countPerLine);
		};

		//[buf_crc=0]:buf data crc
		//[pos=0]辅助定位的number
		self.checkAll = function (rOffset, wOffset, capacity, buf_crc, pos) {
			buf_crc = pi.util.DV(buf_crc, 0);
			pos = pi.util.DV(pos, 0);
			if (self.rOffset !== rOffset || self.wOffset !== wOffset || self.buf.byteLength !== capacity || (buf_crc !== 0 && buf_crc !== pi.jsbuffer.getCrc32(self.buf, capacity))) {
				var input = pi.string.formatAuto("\n[rOffset=%s, wOffset=%s, capactiy=%s, buf_crc=%s]", rOffset, wOffset, capacity, buf_crc);
				pi.debug.assert(false, pos, "\n" + self.toString(buf_crc) + input);
			}
		};

		//将容量扩充times倍(:1.5)
		self.expand = function (times) {
			var newCap;
			times = pi.util.DV(times, 1.5);
			if (times <= 1) {
				times += 1;
			}
			if (times > 1) {
				newCap = (self.wOffset - self.rOffset) * times;
				self.setCapacity(newCap);
			}
		};

		//设置容量,保证不低于ByteBuffer_MinCapcity,仅拷贝有效数据
		self.setCapacity = function (newCap) {
			var start, oldLen, oldBuf;
			//logt("setCapacity=" + newCap);
			newCap = pi.util.DV(newCap, ByteBuffer_MinCapcity);
			if (newCap < ByteBuffer_MinCapcity) {
				newCap = ByteBuffer_MinCapcity;
			}
			if (!self.buf) {
				//first time
				self.buf = pi.lang.createArrayBuffer(newCap);
				return;
			}

			start = self.rOffset;
			oldLen = self.wOffset - self.rOffset;
			if (oldLen < 0) {
				oldLen = 0;
			}
			if (newCap < oldLen) {
				newCap = (oldLen + ByteBuffer_MinCapcity) * 1.5;
			}

			oldBuf = self.buf;
			self.buf = pi.lang.createArrayBuffer(newCap);

			if (oldLen > 0) {
				wb(oldBuf, self.buf, pi.vcall.TYPE_BUFFER, 0, start, oldLen);
			}

			//不要使用writeBufferContent,避免循环依赖
			//set new attributes
			self.rOffset = 0;
			self.wOffset = oldLen;
		};

		//获取有效数据的长度(<=0表示无有效数据)
		self.getLength = function () {
			return self.wOffset - self.rOffset;
		};

		//write时校验,如果buf不足则expand; len:追加的长度;
		//暂时不考虑expand失败的情形
		self.checkWrite = function (len) {
			var left, oldLen, capacity = self.buf.byteLength;
			left = capacity - self.wOffset;
			if (left < len) {
				oldLen = self.wOffset - self.rOffset;
				if (oldLen < 0) {
					oldLen = 0;
				}
				self.setCapacity((oldLen + len) * 1.5);
			}
		};

		//read时检验,可以读取垃圾数据:rOffset>wOffset,但不能读取越界:rOffset>buf.byteLength
		self.checkRead = function (toReadBytes, type, chagneROffset) {
			var newOffset = self.rOffset + toReadBytes;
			if (newOffset > self.buf.byteLength) {
				log("bb error, out of bound: newOffset=" + newOffset + ", [type=" + type + ", " + self.toString() + "]");
				return false;
			} else {
				if (chagneROffset) {
					self.rOffset = newOffset;
				}
				return true;
			}
		};

		//////////////////////////////////////////////////////////////////////////read/write functions
		//不需要readBuffer
		self.writeBufferContent = function (buf, start, len) {
			if (start < 0 || len < 0) {
				log(pi.string.formatAuto("bb error, start=%s, len=%s", start, len));
				return 0;
			}
			if (len) {
				if (wb(buf, self.buf, pi.vcall.TYPE_BUFFER, self.wOffset, start, len) === len) {
					self.wOffset += len;
					return len;
				} else {
					log(pi.string.formatAuto("bb error, writeBufferContent fail, start=%s, len=%s", start, len));
				}
			}
			return 0;
		};

		//写入buf[bufOffset]的地址.
		self.writeBufferPointer = function (buf, bufOffset) {
			self.checkWrite(4);
			var r = wb(buf, self.buf, pi.vcall.TYPE_POINTER, self.wOffset, bufOffset);
			if (r === 4) {
				self.wOffset += 4;
			}
			return r;
		};

		//write string core, 区分c/java & 不同编码
		self.writeStringCore = function (str, nullTerminate, bAnsi) {
			var c, type, writeBytes, writeChars;
			nullTerminate = pi.util.DV(nullTerminate, true);
			if (!(str instanceof String)) {
				str = String(str);
			}
			writeChars = nullTerminate ? -1 : str.length;
			//java空字符串("")直接返回(ok)
			if (writeChars === 0) {
				return 0;
			}
			if (bAnsi) {
				type = pi.vcall.TYPE_ANSI_STRING;
				writeBytes = pi.os.util.ansiLength(str);
				if (nullTerminate) {
					writeBytes++;
				}
			} else {
				type = pi.vcall.TYPE_STRING;
				writeBytes = str.length * 2;
				if (nullTerminate) {
					writeBytes += 2;
				}
			}

			self.checkWrite(writeBytes);
			c = wb(str, self.buf, type, self.wOffset, writeChars);
			if (c) {
				self.wOffset += c;
			} else {
				log(pi.string.formatAuto("bb error, writeString fail, writeBytes=%s, writeChars=%s, type=%s", writeBytes, writeChars, type));
			}
			return c;
		};

		//write ansi string(cast to string first),nullTerminate:true
		self.wsa = self.writeStringA = function (str, nullTerminate) {
			return self.writeStringCore(str, nullTerminate, true);
		};

		//write unicode string(cast to string first),nullTerminate:true
		self.ws = self.writeString = function (str, nullTerminate) {
			return self.writeStringCore(str, nullTerminate, false);
		};

		//read ansi string
		self.rsa = self.readStringA = function (readBytes) {
			readBytes = pi.util.DV(readBytes, -1);
			var newOffset, str = rb(self.buf, pi.vcall.TYPE_ANSI_STRING, self.rOffset, readBytes);
			if (self.buf.rOffset !== -1) {
				newOffset = self.buf.rOffset; //c层设置
				if (self.checkRead(newOffset - self.rOffset, "readStringA", true)) {
					return str;
				}
			}
		};

		//read unicode string
		self.rs = self.readString = function (readBytes) {
			var str, toReadBytes;
			readBytes = pi.util.DV(readBytes, -1);
			str = rb(self.buf, pi.vcall.TYPE_STRING, self.rOffset, readBytes);
			if (typeof str !== "undefined") {
				toReadBytes = str.length * 2 + (readBytes === -1 ? 2 : 0);
				if (self.checkRead(toReadBytes, "readString", true)) {
					return str;
				}
			}
		};

		//write number core
		self.writeNumberCore = function (val, type, nbytes) {
			var r;
			self.checkWrite(nbytes);
			r = wb(val, self.buf, type, self.wOffset);
			if (r === nbytes) {
				self.wOffset += nbytes;
			}
			return r;
		};

		//read number core
		self.readNumberCore = function (type, nbytes) {
			if (self.checkRead(nbytes, type)) {
				var r = rb(self.buf, type, self.rOffset);
				self.rOffset += nbytes;
				return r;
			}
		};

		//write byte/ubyte
		self.wb = self.writeByte = function (val) {
			return self.writeNumberCore(val, pi.vcall.TYPE_BYTE, 1);
		};

		//read byte
		self.rb = self.readByte = function () {
			return self.readNumberCore(pi.vcall.TYPE_BYTE, 1);
		};

		//read ubyte
		self.rub = self.readUbyte = function () {
			return self.readNumberCore(pi.vcall.TYPE_UBYTE, 1);
		};

		//write short/ushort
		self.wsh = self.writeShort = function (val) {
			return self.writeNumberCore(val, pi.vcall.TYPE_SHORT, 2);
		};

		//read short
		self.rsh = self.readShort = function () {
			return self.readNumberCore(pi.vcall.TYPE_SHORT, 2);
		};

		//read ushort
		self.rush = self.readUshort = function () {
			return self.readNumberCore(pi.vcall.TYPE_USHORT, 2);
		};

		//write int/uint
		self.wi = self.wui = self.writeInt = function (val) {
			return self.writeNumberCore(val, pi.vcall.TYPE_INT, 4);
		};

		//read int
		self.ri = self.readInt = function () {
			return self.readNumberCore(pi.vcall.TYPE_INT, 4);
		};

		//read uint
		self.rui = self.readUint = function () {
			return self.readNumberCore(pi.vcall.TYPE_UINT, 4);
		};

		//write long/ulong
		self.wl = self.writeLong = function (val) {
			return self.writeNumberCore(val, pi.vcall.TYPE_LONG, 8);
		};

		//read long
		self.rl = self.readLong = function () {
			return self.readNumberCore(pi.vcall.TYPE_LONG, 8);
		};

		//read ulong
		self.rul = self.readUlong = function () {
			return self.readNumberCore(pi.vcall.TYPE_ULONG, 8);
		};

		//write double
		self.wd = self.writeDouble = function (val) {
			return self.writeNumberCore(val, pi.vcall.TYPE_DOUBLE, 8);
		};

		//read double
		self.rd = self.readDouble = function () {
			return self.readNumberCore(pi.vcall.TYPE_DOUBLE, 8);
		};

		//write float
		self.wf = self.writeFloat = function (val) {
			return self.writeNumberCore(val, pi.vcall.TYPE_FLOAT, 4);
		};

		//read float
		self.rf = self.readFloat = function () {
			return self.readNumberCore(pi.vcall.TYPE_FLOAT, 4);
		};

		//this constructor code needs to put behide setCapacity definition, so we put this at last
		self.setCapacity(capacity);
		self.rOffset = self.wOffset = 0;
		self.maxLength = (maxLength || maxLength < 0) ? maxLength : 0x7fffffff;
		if (typeof initValue !== "undefined") {
			pi.jsbuffer.fillBuffer(self.buf, initValue);
		}
	};

	// js ByteBuffer wrapper
	module = function (capacity, maxLength, initValue) {
		return new ByteBuffer(capacity, maxLength, initValue);
	};

	return module;
});